package device

import (
	"context"
	"errors"
	"fmt"
	"github.com/tarm/serial"
	"github.com/wj008/goyee/config"
	log "github.com/wj008/goyee/logger"
	"sync"
	"time"
)

// SerialPort 串口设备对象
type SerialPort struct {
	sync.Mutex
	address     string
	baudRate    int
	dataBits    int
	stopBits    int
	parity      string
	readTimeout time.Duration
	chSend      chan []byte
	chSendOpen  bool
	isRunning   bool
	callback    DataCallback
	port        *serial.Port
}

// NewSerialPort 创建新的串口设备实例，接收具体参数
func NewSerialPort(address string, baudRate, dataBits, stopBits int, parity string) *SerialPort {
	return &SerialPort{
		address:     address,
		baudRate:    baudRate,
		dataBits:    dataBits,
		stopBits:    stopBits,
		parity:      parity,
		readTimeout: 100 * time.Millisecond,
	}
}

// NewSerialPortDefault 创建使用默认配置的串口设备实例
func NewSerialPortDefault() *SerialPort {
	// 使用默认配置
	address := config.String("serial_addr", "/dev/ttyUSB0")
	baudRate := config.Int("serial_rate", 115200)
	dataBits := config.Int("serial_data_bits", 8)
	stopBits := config.Int("serial_stop_bits", 1)
	parity := config.String("serial_parity", "N")
	return NewSerialPort(address, baudRate, dataBits, stopBits, parity)
}

// Run 启动串口服务，包含自动重连机制
func (sp *SerialPort) Run(ctx context.Context, cb DataCallback) {
	sp.callback = cb
	timer := 1
	for {
		select {
		case <-ctx.Done():
			log.LPrintf(2, "串口 %s: 服务被终止", sp.address)
			sp.Stop()
			return
		case <-time.After(time.Duration(timer) * time.Second):
			timer = 5 // 重连间隔，第一次1秒，之后5秒
			log.LPrintf(2, "串口 %s: 尝试启动服务", sp.address)
			if err := sp.setup(ctx); err != nil {
				// 区分处理超时错误和其他错误
				log.LPrintf(2, "串口 %s: 启动失败: %v", sp.address, err)
			}
		}
	}
}

// Stop 停止串口服务
func (sp *SerialPort) Stop() {
	sp.Lock()
	defer sp.Unlock()
	sp.isRunning = false
	if sp.port != nil {
		_ = sp.port.Close()
	}
	if sp.chSendOpen {
		close(sp.chSend)
		sp.chSendOpen = false
	}
}

// setup 初始化串口连接，使用子上下文管理
func (sp *SerialPort) setup(parentCtx context.Context) error {
	// 创建子上下文，用于管理当前setup的生命周期
	sp.Lock()
	sp.chSend = make(chan []byte, 10)
	sp.chSendOpen = true
	sp.Unlock()

	ctx, cancel := context.WithCancel(parentCtx)
	defer cancel() // 确保退出时取消子上下文
	cfg := &serial.Config{
		Name:        sp.address,
		Baud:        sp.baudRate,
		Size:        byte(sp.dataBits),
		ReadTimeout: 1 * time.Second,
	}
	switch sp.stopBits {
	case 1:
		cfg.StopBits = serial.Stop1
	case 2:
		cfg.StopBits = serial.Stop2
	default:
		cfg.StopBits = serial.Stop1
	}

	switch sp.parity {
	case "N", "n":
		cfg.Parity = serial.ParityNone
	case "O", "o":
		cfg.Parity = serial.ParityOdd
	case "E", "e":
		cfg.Parity = serial.ParityEven
	default:
		cfg.Parity = serial.ParityNone
	}
	port, err := serial.OpenPort(cfg)
	if err != nil {
		return fmt.Errorf("打开串口失败: %w", err)
	}
	log.LPrintf(2, "串口 %s: 启动成功", sp.address)
	sp.port = port
	sp.isRunning = true
	sp.chSend = make(chan []byte) // 重新创建发送通道
	// 用于接收子协程的错误
	errChan := make(chan error, 2)
	// 启动读取协程
	go func() {
		err := sp.readFromPort(ctx)
		if err != nil {
			errChan <- fmt.Errorf("读取协程错误: %w", err)
		}
	}()
	// 启动写入处理协程
	go func() {
		err := sp.processWrites(ctx)
		if err != nil {
			errChan <- fmt.Errorf("写入协程错误: %w", err)
		}
	}()
	// 监控子上下文和错误通道
	select {
	case <-ctx.Done():
		// 父上下文被取消
		sp.Stop()
		return ctx.Err()
	case err := <-errChan:
		sp.Stop()
		return err
	}
}

// readFromPort 从串口读取数据，发生错误时返回
func (sp *SerialPort) readFromPort(ctx context.Context) error {
	buf := make([]byte, 1)
	var dataBuffer []byte
	lastReceiveTime := time.Now()
	for {
		select {
		case <-ctx.Done():
			if len(dataBuffer) > 0 {
				sp.sendReceivedData(dataBuffer)
			}
			return ctx.Err()
		default:
			if time.Since(lastReceiveTime) > sp.readTimeout && len(dataBuffer) > 0 {
				sp.sendReceivedData(dataBuffer)
				dataBuffer = nil
				lastReceiveTime = time.Now()
			}
			n, err := sp.port.Read(buf[:])
			if err != nil {
				continue
			}
			if n > 0 {
				dataBuffer = append(dataBuffer, buf[:n]...)
				lastReceiveTime = time.Now()
			}
		}
	}
}

// processWrites 处理发送数据，发生错误时返回
func (sp *SerialPort) processWrites(ctx context.Context) error {
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case data := <-sp.chSend:
			if _, err := sp.port.Write(data); err != nil {
				// 返回写入错误，会被setup函数捕获
				return fmt.Errorf("写入数据失败: %w", err)
			}
		}
	}
}

// sendReceivedData 处理接收的数据并调用回调
func (sp *SerialPort) sendReceivedData(data []byte) {
	if sp.callback == nil {
		return
	}
	// 创建数据副本，避免缓冲区数据被覆盖
	dataCopy := make([]byte, len(data))
	copy(dataCopy, data)
	// 调用回调函数
	sp.callback(dataCopy, sp.address)
}

// SendData 发送数据到串口
func (sp *SerialPort) SendData(data []byte) error {
	sp.Lock()
	defer sp.Unlock()
	if !sp.isRunning || !sp.chSendOpen {
		return errors.New("串口未运行")
	}
	select {
	case sp.chSend <- data:
		return nil
	case <-time.After(1 * time.Second):
		return errors.New("发送数据超时")
	}
}

// GetLocalAddr 获取本地地址
func (sp *SerialPort) GetLocalAddr() string {
	return sp.address
}

// GetRemoteAddr 获取远程服务器地址
func (sp *SerialPort) GetRemoteAddr() string {
	return ""
}

// IsRunning 检查客户端是否正在运行
func (sp *SerialPort) IsRunning() bool {
	sp.Lock()
	defer sp.Unlock()
	return sp.isRunning
}
